package com.mfs.proxy.core.enums;

import com.mfs.common.utills.ObjectUtil;
import com.mfs.common.utills.StringUtil;
import com.mfs.proxy.core.client.config.ClientConfig;
import com.mfs.proxy.core.client.ProxyClient;
import com.mfs.proxy.core.client.reactor.SubClientReactor;
import com.mfs.proxy.core.message.mailbox.DefaultCommunicationMailbox;
import com.mfs.proxy.core.message.processor.MessageReceiveProcessor;
import com.mfs.proxy.core.message.processor.MessageSendProcessor;
import com.mfs.proxy.core.server.endpoint.ProxyHandle;
import com.mfs.proxy.core.util.ProxyTaskExecutor;
import com.mfs.proxy.core.message.*;
import com.mfs.proxy.core.server.endpoint.ProxyEndpoint;
import com.mfs.proxy.core.server.reactor.ProxyReactor;
import com.mfs.proxy.core.server.registry.AbstractTimeoutProxyClientRegistry;
import com.mfs.proxy.core.server.registry.ProxyClientRegistry;
import com.mfs.proxy.core.server.registry.ProxyClientRegistrySingletonFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

/**
 * 代理服务端、代理客户端通信的消息类型枚举类
 * 每一个消息类型，有固定的发送方、接收方、消息内容、消息发送处理器、消息接收处理器
 * @author mfs
 * @date 2022/07/17
 * */
public enum MessageTypeEnum {

    /**
     * 发送方：代理服务端
     * 接收方：代理客户端
     * 消息内容：代理句柄
     * */
    CONNECTED(2, "连接建立成功", new MessageSendProcessor<ProxyClient>() {

    }, (MessageReceiveProcessor<ProxyHandle>) (selectionKey, proxyHandle) -> {
        ClientConfig.getInstance().setProxyHandle(proxyHandle);
    }),

    /**
     * 发送方：代理客户端
     * 接收方：代理服务端
     * 消息内容：代理客户端配置信息，请求分配代理地址
     * */
    CONNECT(1, "建立连接", new MessageSendProcessor<ClientConfig>() {
        @Override
        public void send(SelectionKey selectionKey, MessageTypeEnum messageTypeEnum, ClientConfig messageContent) throws IOException {
            SocketChannel communicationChanel = (SocketChannel)selectionKey.channel();
            ByteBuffer byteBuffer = ByteBuffer.wrap(ObjectUtil.serialize(new Message(messageTypeEnum.getType(), messageContent)));
            communicationChanel.write(byteBuffer);
            selectionKey.interestOps(SelectionKey.OP_READ);
        }
    }, (MessageReceiveProcessor<ClientConfig>) (clientSelectKey, clientConfig) -> {
        ProxyEndpoint endpoint = ProxyReactor.register();
        if (endpoint == null) {
            clientSelectKey.channel().close();
            clientSelectKey.cancel();
            return;
        }

        ProxyClient proxyClient = new ProxyClient(clientConfig, new DefaultCommunicationMailbox(clientSelectKey), endpoint, ProxyTaskExecutor.getInstance());
        if (!ProxyClientRegistrySingletonFactory.getProxyClientRegistry().register(endpoint.getProxyIp(), endpoint.getProxyPort(), proxyClient)) {
            clientSelectKey.cancel();
            return;
        }
        ProxyHandle proxyHandle = new ProxyHandle(endpoint.getProxyIp(), endpoint.getProxyPort());
        proxyClient.getCommunicationMailbox().send(MessageTypeEnum.CONNECTED, proxyHandle);
    }),

    /**
     * 发送方：代理客户端
     * 接收方：代理服务端
     * 消息内容：{@link ProxyHandle}
     * */
    HEART_BEAT(3, "心跳", new MessageSendProcessor<ProxyHandle>() {
    }, (MessageReceiveProcessor<ProxyHandle>) (clientSelectKey, proxyHandle) -> {
        ProxyClientRegistry clientRegistry = ProxyClientRegistrySingletonFactory.getProxyClientRegistry();
        if (clientRegistry instanceof AbstractTimeoutProxyClientRegistry && StringUtil.isNotBlank(proxyHandle.getProxyIp())
                && proxyHandle.getProxyPort() != null) {
            ((AbstractTimeoutProxyClientRegistry) clientRegistry).hello(proxyHandle.getProxyIp(), proxyHandle.getProxyPort());
        }
    }),

    /**
     * 代理服务端收到代理请求信息后，发送改消息，向代理客户端请求一个通道来进行处理改请求
     * 发送方：代理服务端
     * 接收方：代理客户端
     * 消息内容：请求号，用户通道建立时确认改通道是为哪个请求搭建的
     * */
    CHANNEL_REQUEST(4, "通道请求", new MessageSendProcessor<String>() {
    }, (MessageReceiveProcessor<String>) (serverSelectKey, requestNo) -> {
        SocketChannel proxyChannel = SocketChannel.open(new InetSocketAddress(ClientConfig.getInstance().getServerIp(),
                ClientConfig.getInstance().getServerPort()));
        proxyChannel.configureBlocking(false);
        SelectionKey selectionKey = SubClientReactor.register(proxyChannel);
        selectionKey.attach(requestNo);
    }),
    /**
     * 代理服务端收到代理请求信息后，发送改消息，向代理客户端请求一个通道来进行处理改请求
     * 发送方：代理客户端
     * 接收方：代理服务端
     * 消息内容：代理句柄+请求号，用户通道建立时确认改通道是为哪个请求搭建的
     * */
    CHANNEL_RESPONSE(5, "通道响应", new MessageSendProcessor<ChannelResponseMessageContent>() {
        @Override
        public void send(SelectionKey selectionKey, MessageTypeEnum messageTypeEnum, ChannelResponseMessageContent messageContent) throws IOException {
            // 不做任何处理，此时的响应消息应该是通过新建立的通道发送
        }
    }, (MessageReceiveProcessor<ChannelResponseMessageContent>) (serverSelectKey, messageContent) -> {
        ProxyHandle proxyHandle = messageContent.getProxyHandle();
        String requestNo = messageContent.getRequestNo();
        SocketChannel proxyChannel = (SocketChannel) serverSelectKey.channel();
        ProxyClient proxyClient = ProxyClientRegistrySingletonFactory.getProxyClientRegistry().checkout(proxyHandle.getProxyIp(), proxyHandle.getProxyPort());
        if (proxyClient == null) {
            return;
        }
        proxyClient.brokered(requestNo, proxyChannel);
        serverSelectKey.cancel();
    }),

    /**
     * 代理客户端主动断开连接
     * 发送方：代理客户端
     * 接收方：代理服务端
     * 消息内容：{@link ProxyHandle}
     * */
    CLOSE(6, "断开连接", new MessageSendProcessor<ProxyHandle>() {
    }, (MessageReceiveProcessor<ProxyHandle>) (clientSelectKey, proxyHandle) -> {
        ProxyClientRegistry clientRegistry = ProxyClientRegistrySingletonFactory.getProxyClientRegistry();
        if (StringUtil.isNotBlank(proxyHandle.getProxyIp())
                && proxyHandle.getProxyPort() != null) {
            clientRegistry.unregister(proxyHandle.getProxyIp(), proxyHandle.getProxyPort());
        }
    });

    private Integer type;
    private String describe;
    private MessageSendProcessor messageSendProcessor;
    private MessageReceiveProcessor messageReceiveProcessor;
    MessageTypeEnum(Integer type, String describe, MessageSendProcessor messageSendProcessor, MessageReceiveProcessor messageReceiveProcessor) {
        this.type = type;
        this.describe = describe;
        this.messageSendProcessor = messageSendProcessor;
        this.messageReceiveProcessor = messageReceiveProcessor;
    }

    public Integer getType() {
        return type;
    }

    public String getDescribe() {
        return describe;
    }

    public MessageReceiveProcessor getMessageReceiveProcessor() {
        return messageReceiveProcessor;
    }

    public MessageSendProcessor getMessageSendProcessor() {
        return messageSendProcessor;
    }

    public static MessageTypeEnum valueOf(Integer type){
        for (MessageTypeEnum messageTypeEnum : MessageTypeEnum.values()) {
            if (messageTypeEnum.type.equals(type)) {
                return messageTypeEnum;
            }
        }
        return null;
    }
}
